#include "xchain/xchain.h"

// 参数由Context提供
class PingPongTraceBack {
public:
    // 初始化写入权限
    // 参数: owner - 具有写入权限的address
    virtual void initialize() = 0;

    // // 添加用户
    virtual void addUser() = 0;

    // // 根据用户查询拥有的乒乓球拍
    virtual void queryPingPongByUser() = 0;

    // // 用户-——乒乓球拍信息绑定
    virtual void bindUserAndPingPong() = 0;

    // // 乒乓球拍信息溯源
    virtual void traceBackPingPong() = 0;

    // // 乒乓球拍鉴伪
    virtual void forgeryPingPong() = 0;

    // // 查询具有写权限的账户
    // // 返回值: 具有写权限的address
    virtual void queryOwner() = 0;
};

struct PingPongTraceBackDemo : public PingPongTraceBack, public xchain::Contract {
private:
    // 定义主键前缀
    const std::string OWNER_KEY = "Owner";
    const std::string USER = "U_";
    const std::string PINGPONG = "P_";
    const std::string USER_PINGPONG = "R1_";
    const std::string PINGPONG_USER = "R2_";

    // 检查调用者是否是合约的拥有者
    bool isOwner(xchain::Context* ctx, const std::string& caller) {
        std::string owner;
        if (!ctx->get_object(OWNER_KEY, &owner)) {
            return false;
        }
        return (owner == caller);
    }

public:
    void initialize() {
        // 获取合约上下文对象
        xchain::Context* ctx = this->context();
        // 从合约上下文中获取合约参数, 由合约部署者指定具有写入权限的address
        std::string owner = ctx->arg("owner");
        if (owner.empty()) {
            ctx->error("missing owner address");
            return;
        }
        // 将具有写入权限的owner地址记录在区块链账本中
        ctx->put_object(OWNER_KEY, owner);
        ctx->ok("success");
    }

    // 添加用户
    void addUser() {
        // 获取合约上下文对象
        xchain::Context* ctx = this->context();

        // 从参数中获取用户主键id，必填参数，没有则返回错误
        const std::string& userid = ctx->arg("userid");
        if (userid.empty()) {
            ctx->error("missing 'userid'");
            return;
        }

        // 从参数中获取用户名username，必填参数，没有则返回错误
        const std::string& username = ctx->arg("username");
        if (username.empty()) {
            ctx->error("missing 'username'");
            return;
        }
        std::string user_key = USER + userid;
        std::string user_value = username;

        // 保存用户信息失败直接返回
        if (!ctx->put_object(user_key, user_value)) {
            ctx->error("failed to save user info");
            return;
        }
        std::string result = "{ userid: " + userid + ", username: " + username + " } save successfully!";

        // 保存成功
        ctx->ok(result);
    }

    // 根据用户查询拥有的乒乓球拍
    void queryPingPongByUser() {
        // 获取合约上下文对象
        xchain::Context* ctx = this->context();
        std::string result;

        // 从参数中获取用户主键id，必填参数，没有则返回错误
        const std::string& userid = ctx->arg("userid");
        if (userid.empty()) {
            ctx->error("missing 'userid'");
            return;
        }

        // 如果userid不存在返回错误
        std::string user_key = USER + userid;
        std::string user_value;
        ctx->get_object(user_key, &user_value);
        if (user_value.empty()) {
            result = "user: " + userid + " is not existing!";
            ctx->error(result);
            return;
        }

        //从账本中将所有的用户——球拍记录查询出来并判断当前球拍是否归当前用户所有
        std::string r1_key = USER_PINGPONG + userid + "_";
        std::unique_ptr<xchain::Iterator> iter = ctx->new_iterator(r1_key, r1_key + "~");
        std::string r1_data;
        while (iter->next()) {
            std::pair<std::string, std::string> res;
            iter->get(&res);
            if (res.first.length() > r1_key.length()) {
                std::string content = res.second;
                if (content == "1") {
                    std::string pingpongid = res.first.substr(r1_key.length(), res.first.length());
                    r1_data += (pingpongid + " ");
                }
            }
        }
        result = "userid: " + userid + " pingpongid: " + r1_data;
        // 执行成功，返回status code 200
        ctx->ok(result);
    }

    // 用户-——乒乓球拍信息绑定
    void bindUserAndPingPong() {
        // 获取合约上下文对象
        xchain::Context* ctx = this->context();
        std::string result;

        // 从参数中获取用户主键id，必填参数，没有则返回错误
        const std::string& userid = ctx->arg("userid");
        if (userid.empty()) {
            ctx->error("missing 'userid'");
            return;
        }

        // 从参数中获取pingpong主键id，必填参数，没有则返回错误
        const std::string pingpongid = ctx->arg("pingpongid");
        if (pingpongid.empty()) {
            ctx->error("missing 'pingpongid'");
        }

        // 如果userid不存在返回错误
        std::string user_key = USER + userid;
        std::string user_value;
        ctx->get_object(user_key, &user_value);
        if (user_value.empty()) {
            result = "user: " + userid + " is not existing!";
            ctx->error(result);
            return;
        }

        // 查询pingpongid目前的拥有者
        std::string last_userid;
        std::string pingpong_key = PINGPONG + pingpongid;
        std::string r1_key = USER_PINGPONG + userid + "_" + pingpongid;
        std::string r2_key = PINGPONG_USER + pingpongid + "_" + userid;
        ctx->get_object(pingpong_key, &last_userid);
        if (last_userid.empty()) {
            // 如果查询为空，这说明乒乓球还未交易，不属于任何人，直接添加相关信息即可
            // 1、添加乒乓球拍信息
            if (!ctx->put_object(pingpong_key, userid)) {
                return;
            }

            // 2、添加用户——球拍信息记录
            if (!ctx->put_object(r1_key, "1")) {
                return;
            }

            // 3、 添加球拍——用户信息记录
            if (!ctx->put_object(r2_key, "product")) {
                return;
            }

            // 返回结果
            result = userid + " produces " + pingpongid + " successfully!";
            ctx->ok(result);
        }
        else {
            // 如果查询结果不为空，这说明乒乓球属于厂商或者用户，需要添加记录以及修改相关信息
            // 1、 修改当前的乒乓球拍拥有者信息
            if (!ctx->put_object(pingpong_key, userid)) {
                return;
            }

            // 2、将目前拥有者改为未拥有者
            std::string last_userid_key = USER_PINGPONG + last_userid + "_" + pingpongid;
            if (!ctx->put_object(last_userid_key, "0")) {
                return;
            }

            // 3、添加用户——球拍记录信息
            if (!ctx->put_object(r1_key, "1")) {
                return;
            }

            // 4、添加球拍——用户记录信息
            if (!ctx->put_object(r2_key, last_userid)) {
                return;
            }

            //返回结果
            result = userid + " buys " + pingpongid + " from " + last_userid + " successfully!";
            ctx->ok(result);
        }
    }

    // 乒乓球拍信息溯源
    void traceBackPingPong() {
        // 获取合约上下文对象
        xchain::Context* ctx = this->context();
        std::string result;

        // 从参数中获取用户主键id，必填参数，没有则返回错误
        const std::string& pingpongid = ctx->arg("pingpongid");
        if (pingpongid.empty()) {
            ctx->error("missing 'pingpongid'");
            return;
        }

        // 如果pingpongid不存在返回错误
        std::string pingpong_key = PINGPONG + pingpongid;
        std::string pingpong_value;
        ctx->get_object(pingpong_key, &pingpong_value);
        if (pingpong_value.empty()) {
            result = "pingpong: " + pingpongid + " is not existing!";
            ctx->error(result);
            return;
        }

        //从账本中将所有的球拍——用户记录查询出来
        std::string r2_key = PINGPONG_USER + pingpongid + "_";
        std::unique_ptr<xchain::Iterator> iter = ctx->new_iterator(r2_key, r2_key + "~");
        std::string r2_data;
        while (iter->next()) {
            std::pair<std::string, std::string> res;
            iter->get(&res);
            if (res.first.length() > r2_key.length()) {
                std::string sell = res.second;
                std::string buy = res.first.substr(r2_key.length(), res.first.length());
                r2_data += "from " + sell + " to " + buy + "\n";
            }
        }
        result = "pingpong: " + pingpongid + "\n" + r2_data;
        // 执行成功，返回status code 200
        ctx->ok(result);
    }

    // 乒乓球拍鉴伪
    void forgeryPingPong() {
        // 获取合约上下文对象
        xchain::Context* ctx = this->context();
        std::string result;

        // 从参数中获取用户主键id，必填参数，没有则返回错误
        const std::string& userid = ctx->arg("userid");
        if (userid.empty()) {
            ctx->error("missing 'userid'");
            return;
        }

        // 从参数中获取用户主键id，必填参数，没有则返回错误
        const std::string& pingpongid = ctx->arg("pingpongid");
        if (pingpongid.empty()) {
            ctx->error("missing 'pingpongid'");
            return;
        }

        // 确认当前乒乓球拍的拥有者信息
        std::string r1_key = USER_PINGPONG + userid + "_" + pingpongid;
        std::string flag;
        ctx->get_object(r1_key, &flag);
        if (flag == "1") {
            result = "Verification succeeded!";
        }
        else {
            result = "Verification failed!";
        }
        ctx->ok(result);
    }

    void queryOwner() {
        // 获取合约上下文对象
        xchain::Context* ctx = this->context();
        std::string owner;
        if (!ctx->get_object(OWNER_KEY, &owner)) {
            // 没查到owner信息，可能
            ctx->error("get owner failed");
            return;
        }
        // 执行成功，返回owner address
        ctx->ok(owner);
    }
};

DEFINE_METHOD(PingPongTraceBackDemo, initialize) { self.initialize(); }
DEFINE_METHOD(PingPongTraceBackDemo, addUser) { self.addUser(); }
DEFINE_METHOD(PingPongTraceBackDemo, queryPingPongByUser) { self.queryPingPongByUser(); }
DEFINE_METHOD(PingPongTraceBackDemo, bindUserAndPingPong) { self.bindUserAndPingPong(); }
DEFINE_METHOD(PingPongTraceBackDemo, traceBackPingPong) { self.traceBackPingPong(); }
DEFINE_METHOD(PingPongTraceBackDemo, forgeryPingPong) { self.forgeryPingPong(); }
DEFINE_METHOD(PingPongTraceBackDemo, queryOwner) { self.queryOwner(); }